/*
 * Funny Language - a free style programming language.
 * Copyright (C) 2015 by fanguangping (fanguangping@163.com)
 * string.c
 */

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "string.h"
#include "../base/constants.h"
#include "../base/cmemory.h"

String alloc_string(int size) {
	return (String) sys_malloc(sizeof(char) * size);
}

String *alloc_string_array(int size) {
	return (String*) malloc(sizeof(String) * size);
}

String realloc_string(String str, int size) {
	String new_str = (String) sys_realloc(str, size * sizeof(char));
	if (new_str != NULL) {
		str = new_str;
	}
	return str;
}

String init_string() {
	String result = alloc_string(1);
	result[0] = '\0';
	return result;
}

String copy_string(String str) {
	String result = alloc_string(strlen(str) + 1);
	strcpy(result, str);
	return result;
}

String trim_string(String str) {
	char *end;

	while (isspace(*str))
		str++;

	if (*str == '\0')
		return str;

	end = str + strlen(str) - 1;
	while (end > str && isspace(*end))
		end--;

	*(end + 1) = '\0';

	return str;
}

String inner_string(String str) {
	String copyed = copy_string(str);
	if (strlen(copyed) <= 2) {
		return NULL;
	}
	char *p = copyed;
	while(*(p + 1)) p++;
	*p = '\0';
	return copyed + 1;
}

String substr_before(String str, char c) {
	char *p = strchr(str, c);
	if (p) {
		*p = '\0';
		return str;
	} else {
		return "";
	}
}

String substr_between(String str, char a, char b) {
	char *s = strchr(str, a);
	char *e = strchr(str, b);

	if (s && e && s < e) {
		*e = '\0';
		return s + 1;
	} else {
		return "";
	}
}

String substr_after(String str, char c) {
	char *p = strchr(str, c);
	if (p)
		return p + 1;
	else
		return "";
}

String dyna_strcat(String str, String append) {
	if (append == NULL || equals_string(append, "")) {
		return str;
	}
	int length = strlen(str) + strlen(append) + 1;
	str = realloc_string(str, length);
	str = strcat(str, append);
	return str;
}

String append_char(String str, char c) {
	char *p = NULL;
	int length = strlen(str) + 2;
	str = realloc_string(str, length);
	p = str;
	while (*p)
		p++;
	*p++ = c;
	*p++ = '\0';
	return str;
}

String* split_string(String string, const char delimiter) {
	char** result = 0;
	int count = 0;
	char* tmp = string;
	char* last_comma = 0;
	char delim[2];
	delim[0] = delimiter;
	delim[1] = 0;

	/* Count how many elements will be extracted. */
	while (*tmp) {
		if (delimiter == *tmp) {
			count++;
			last_comma = tmp;
		}
		tmp++;
	}

	/* Add space for trailing token. */
	count += last_comma < (string + strlen(string) - 1);

	/* Add space for terminating null string so caller
	 knows where the list of returned strings ends. */
	count++;

	result = sys_malloc(sizeof(char*) * count);

	if (result) {
		int index = 0;
		char* token = strtok(string, delim);

		while (token) {
			//assert(index < count);
			*(result + index++) = strdup(token);
			token = strtok(0, delim);
		}
		//assert(index == count - 1);
		*(result + index) = 0;
	}

	return result;
}

//TODO
String* split_strings(String string, const char* delimiter) {
	char** r = NULL;
	int i = 0;
	String token;
	while (token = strsep(&string, delimiter)) {
		r = (char**)sys_realloc(r, (i + 1) * sizeof(char*));
		*(r + i) = strdup(token);
		i++;
	}
	r = (char**)sys_realloc(r, i * sizeof(char*));
	*(r + i) = NULL;
    return r;
}

//TODO
String* split_string_with_escape(String string, const char delimiter, const char escape) {
	return NULL;
}

String* fetch_string_from_paren_with_escape(String string, const char open, const char close, const char escape) {
	char *str = copy_string(string);
	char *start = str;
	char *end = str;
	String* result = (String *)sys_malloc(100 * sizeof(String));

	String* p =result;
	while(*str) {
		if (*str == escape && *(str + 1)) {
			str = str + 2;
			continue;
		}
		if (*str == open) {
			start = str;
		} else if (*str == close) {
			end = str;
		}
		if (start < end && *start) {
			*end = 0;
			*p = start + 1;
			p++;
			str = end;
			start = str;
		}
		str++;
	}
	*p = NULL;

    return result;
}

String* split_string_once(String string, const char delimiter) {
	char** result = NULL;
	char *p = strchr(string, delimiter);
	if (p) {
		result = alloc_string_array(3);
		*p = '\0';
		*result = copy_string(string);
		*(result + 1) = copy_string(p + 1);
		*(result + 2) = NULL;
	}

	return result;
}

int equals_string(void *a, void *b) {
	if (a == NULL && b == NULL)
		return TRUE;
	if (a == NULL || b == NULL)
		return FALSE;

	if (strcmp(a, b) == 0) {
		return TRUE;
	} else {
		return FALSE;
	}
}

char *strlwr(char *str) {
  size_t i;
  size_t len = strlen(str);

  for(i=0; i<len; i++)
	  str[i] = tolower((unsigned char)str[i]);

  return str;
}

// You must free the result if result is non-NULL.
char *string_replace(char *orig, char *rep, char *with) {
    char *result; // the return string
    char *ins;    // the next insert point
    char *tmp;    // varies
    int len_rep;  // length of rep (the string to remove)
    int len_with; // length of with (the string to replace rep with)
    int len_front; // distance between rep and end of last rep
    int count;    // number of replacements

    // sanity checks and initialization
    if (!orig || !rep)
        return NULL;
    len_rep = strlen(rep);
    if (len_rep == 0)
        return NULL; // empty rep causes infinite loop during count
    if (!with)
        with = "";
    len_with = strlen(with);

    // count the number of replacements needed
    ins = orig;
    for (count = 0; tmp = strstr(ins, rep); ++count) {
        ins = tmp + len_rep;
    }

    tmp = result = sys_malloc(strlen(orig) + (len_with - len_rep) * count + 1);

    if (!result)
        return NULL;

    // first time through the loop, all the variable are set correctly
    // from here on,
    //    tmp points to the end of the result string
    //    ins points to the next occurrence of rep in orig
    //    orig points to the remainder of orig after "end of rep"
    while (count--) {
        ins = strstr(orig, rep);
        len_front = ins - orig;
        tmp = strncpy(tmp, orig, len_front) + len_front;
        tmp = strcpy(tmp, with) + len_with;
        orig += len_front + len_rep; // move to next "end of rep"
    }
    strcpy(tmp, orig);
    return result;
}
